import keras
from keras.datasets import mnist
import numpy as np
import math
from matplotlib import pyplot as plt
from skimage.measure import compare_mse
import PIL
import cv2
from functools import reduce
import random as r
class Image(object):
def __init__(self, path):
self.path = path
self.rgb_image = None
self.bgr_image = None
self.gray_image = None
def read_image(self, return_image = False):
self.rgb_image = plt.imread(self.path)
if return_image:
return self.rgb_image
def bgr(self, return_image = False):
self.bgr_image = np.flip(plt.imread(self.path), 2)
if return_image:
return self.bgr_image
def gray(self, return_image = False):
self.gray_image = cv2.cvtColor(plt.imread(self.path), cv2.COLOR_RGB2GRAY)
if return_image:
return self.gray_image
@staticmethod
def show(image, title = 'image'):
if len(image.shape) == 3:
plt.imshow(image)
else:
plt.imshow(image, cmap = 'gray')
plt.title(title)
@staticmethod
def show_all(image_list, title_list):
assert len(image_list) == len(title_list), "Incompatible lengths of lists!"
N = len(image_list)
plt.figure(figsize=[20, 20])
for i in range(N):
plt.subplot(1, N, i + 1)
Image.show(image_list[i], title_list[i])
plt.show()
class ManipulateImage:
@staticmethod
def flip(image, axis):
return np.flip(image, axis)
@staticmethod
def rotate(image, angle):
image_center = tuple(np.array(image.shape[1::-1]) / 2)
rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR)
return result
@staticmethod
def gaussian_blur(img, ksize, sigma):
dst = cv2.GaussianBlur(img,ksize,sigma,cv2.BORDER_DEFAULT)
return dst
@staticmethod
def median_blur(img, ksize):
dst = cv2.medianBlur(img, ksize)
return dst
@staticmethod
def my_median_blur(img, ksize):
result = np.copy(img)
m = ksize//2
for i in range(m, img.shape[0] - m):
for j in range(m, img.shape[1] - m):
for c in range(3):
sub_img = img[i:i+ksize, j:j+ksize, c]
median = np.median(sub_img)
result[i][j][c] = median
return result
@staticmethod
def crop(img, start, h, w):
if start[0]+h >= img.shape[0]:
h = img.shape[0] - start[0] - 1
if start[1]+w >= img.shape[1]:
w = img.shape[1] - start[1] - 1
return img[start[0]:start[0]+h, start[1]:start[1]+w]
@staticmethod
def zoom(img, zoom_multiplier):
w = img.shape[1]
h = img.shape[0]
width = int(w * 1/zoom_multiplier)
height = int(h * 1/zoom_multiplier)
dim = (w, h)
new_img = ManipulateImage.crop(img, ((h//2) - (height//2), (w//2) - (width//2)), width, height)
# resize image
resized = cv2.resize(new_img, dim, interpolation = cv2.INTER_AREA)
return resized
class RandomAugmentation(ManipulateImage):
def generate_augmentations(img, count, probabilities=0.5):
import random as r
def choose_subgroup(arr, probability):
subgroup = []
probs = [r.uniform(0, 1) < probability for i in range(6)]
i = 0
for boolean in probs:
if boolean:
subgroup.append(i)
if i==4:
break
i += 1
return subgroup
def augment_by_subgroup(img, sub):
functions = [ManipulateImage.flip, ManipulateImage.rotate, ManipulateImage.gaussian_blur, ManipulateImage.median_blur, ManipulateImage.zoom, ManipulateImage.crop]
current_img = np.copy(img)
if 0 in sub:
random_axis = r.randint(0, 2)
current_img = (functions[0](current_img, random_axis))
if 1 in sub:
random_angle = r.randint(0, 180)
current_img = (functions[1](current_img, random_angle))
if 2 in sub:
ksize = 2 * r.randint(1, 3) + 1
sigma = r.randint(1, 15)
current_img = (functions[2](current_img, (ksize, ksize), sigma))
if 3 in sub:
ksize = 2 * r.randint(1, 3) + 1
current_img = (functions[3](current_img, ksize))
if 4 in sub:
zoom_multiplier = r.randint(1, 2)
current_img = (functions[4](current_img, zoom_multiplier))
if 5 in sub:
start = (r.randint(0,img.shape[0]//5),r.randint(0,img.shape[1]//5))
h = r.randint(img.shape[0]//1.5, img.shape[0])
current_img = (functions[5](current_img, start, h, h))
return current_img
augmentations = []
for i in range(count):
subgroup = choose_subgroup([0, 1, 2, 3, 4, 5], probabilities)
augmentations.append(augment_by_subgroup(img, subgroup))
return augmentations
(x_train, y_train), (x_test, y_test) = mnist.load_data()
def print_digits(examples, labels):
# Prints some sorted ten digit images from a large dataset.
count = 0
ten_examples, ten_labels = list(), list()
for i in range(len(labels)):
if count > 9:
break
if labels[i] == count:
count += 1
ten_examples.append(examples[i])
ten_labels.append(labels[i])
Image.show_all(ten_examples, ten_labels)
print("Train:")
print_digits(x_train, y_train)
print("\n\nTest:")
print_digits(x_test, y_test)
def crop_img(gray_img):
# Crops a given gray image according to its bounderies,
# Returns the cropped image.
ret, thresh = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
contour_areas = list(map(lambda cnt: cv2.contourArea(cnt), contours))
max_area_cnt = list(filter(lambda cnt: cv2.contourArea(cnt) == np.max(contour_areas), contours))[0]
peri = cv2.arcLength(max_area_cnt, True)
approx = cv2.approxPolyDP(max_area_cnt, 0.02 * peri, True)
x, y, w, h = cv2.boundingRect(approx)
return gray_img[y:y+h, x:x+w]
def crop_images(img_lst):
# Crops a given list of images,
# Returns the cropped images as a new list.
return list(map(lambda img: crop_img(img), img_lst))
cropped_x_train = crop_images(x_train)
cropped_x_test = crop_images(x_test)
print("Cropped train:")
print_digits(cropped_x_train, y_train)
print("\n\nCropped test:")
print_digits(cropped_x_test, y_test)
def list_images_by_label(tag_label, examples, labels):
# Returns a list of images correspinding with the given label out of
# a given dataset of examples.
new_lst = list()
for i in range(len(labels)):
if labels[i] == tag_label:
new_lst.append(examples[i])
return new_lst
labels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Defining two lists of lists of sorted images according to its labels.
sorted_digit_train_images = list(map(lambda label: list_images_by_label(label, cropped_x_train, y_train), labels))
#[print(len(lst)) for lst in sorted_digit_train_images]
sorted_digit_test_images = list(map(lambda label: list_images_by_label(label, cropped_x_test, y_test), labels))
#[print(len(lst)) for lst in sorted_digit_test_images]
# Globals
n = 101
m = 6000
number_of_digits = 3
def convert_number_to_digits(number):
# Converts a given number to its digits,
# Returns the number's digits as a list.
temp = number
digits = list()
while temp > 0:
digits.insert(0, int(temp % 10))
temp = int(temp / 10)
return digits
def pad_lst_by_zeroes(lst, n):
# Pads a given list with zeroes to achieve a given number of elements- n.
lst = list(lst)
if n > len(lst):
number_of_zeroes = n - len(lst)
for i in range(number_of_zeroes):
lst.insert(0, 0)
return lst
def remove_array(outer_arr, inner_arr):
# For ndarrays inside anoher array.
index = 0
size = len(outer_arr)
while index != size and not np.array_equal(outer_arr[index], inner_arr):
index += 1
if index != size:
outer_arr.pop(index)
else:
raise ValueError('array not found in list.')
def paste_images_horizontally(images_lst, horizontal_gap, vertical_gap, padding):
# Pastes images horizontally into a new image, by a given list of images
# and the spaces between the images,
# Returns the new image that created by concatinating all the images in the given list
total_width = images_lst[0].shape[1] + images_lst[1].shape[1] + images_lst[2].shape[1] + horizontal_gap * 2 + padding * (len(images_lst)-1)
max_height = max(images_lst[0].shape[0], images_lst[1].shape[0], images_lst[2].shape[0]) + vertical_gap * 2
new_img = np.zeros((max_height, total_width))
positions = [vertical_gap, horizontal_gap]
for img in images_lst:
h = img.shape[0]
w = img.shape[1]
new_img[positions[0]:positions[0]+h, positions[1]:positions[1] + w] = img
positions[1] += (w + padding)
return new_img
def number_img_boundaries(number_gray_img):
# Creates a new image contains the given image and it's bounderies,
# Returns this new image.
global number_of_digits
new_img = np.zeros(number_gray_img.shape)
temp_gray_image = cv2.normalize(number_gray_img, None, alpha=0, beta=1,
norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1)
ret, thresh = cv2.threshold(temp_gray_image, 0, 255, cv2.THRESH_BINARY)
thresh = (thresh * 255).astype(np.uint8)
contours, hierarchy = cv2.findContours(thresh,
cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(new_img, contours, -1, (255, 255, 255), 1)
return new_img
def create_new_img_examples(number_lst, sorted_digit_images):
"""
Creates m = 6000 different examples that represents a specific number,
which given by a list lst that includes three images of three digits.
Returns the m = 6000 images dataset of the corresponding number.
"""
global m
lsb0 = number_lst[0]
lsb1 = number_lst[1]
lsb2 = number_lst[2]
combinations = []
for num_image0 in sorted_digit_images[lsb0]:
for num_image1 in sorted_digit_images[lsb1]:
for num_image2 in sorted_digit_images[lsb2]:
if len(combinations) == m:
return combinations
combinations.append([num_image0, num_image1, num_image2])
return combinations
def compose_labels_by_lst(sorted_digit_images):
# Composes a dataset - list of m = 6000 uniqe image examples,
# for each number between 0 to n.
# Returns a tuple includes a list of the results, and a list of the corresponding labels
# sorted_digit_images is a list of sorted lists of digit images (sorted by digit labels)
global n, m, number_of_digits
new_sorted_examples_as_lists = list()
new_sorted_sampples, new_labels = list(), list()
for number in range(0, n, 1):
digits = convert_number_to_digits(number)
digits = pad_lst_by_zeroes(digits, number_of_digits)
number_examples = create_new_img_examples(digits, sorted_digit_images)
new_sorted_examples_as_lists.append(number_examples)
for number in range(n):
for i in range(m):
current_example = paste_images_horizontally(new_sorted_examples_as_lists[number][i], 5, 5, 3)
new_sorted_sampples.append(current_example)
new_labels.append(number)
return new_sorted_sampples, new_labels
new_examples, new_labels = compose_labels_by_lst(sorted_digit_train_images)
for i in range(0, 600000, 6000):
Image.show_all(new_examples[i:i+5], new_labels[i:i+5])
temp_img = number_img_boundaries(new_examples[600000])
Image.show(temp_img)
# AUGMENTATIONS OF 3 DIGIT NUMBERS
number_examples = []
for i in range(20):
index = r.randint(0, 599999)
img = cv2.cvtColor(new_examples[index].astype(np.uint8),cv2.COLOR_GRAY2RGB)
number_examples.append(img)
aug = RandomAugmentation.generate_augmentations(img, 4, 0.5)
for img2 in aug:
number_examples.append(img2)
for i in range(0, 100, 5):
Image.show_all(number_examples[i:i+5], ["" for i in range(5)])
# flip completely ruins the images
# rotate will work only until 90 and -90 degrees
# zoom is ok
# crop can crop out one number ruining the whole point of a 3-digit number
# blur is ok
#Part 2
class Shapes:
def __init__(self, path):
import random as r
def load_data(self):
# return (x_train, y_train), (x_test, y_test)
pass
@staticmethod
def create_ellipse():
center = (r.randint(20, 30), r.randint(20, 30))
min_center = min(center)
ellipse = np.zeros((50, 50, 3),np.uint8)
axesLength = (r.randint(5, min_center), r.randint(5, min_center))
angle = r.randint(0, 360)
startAngle = 0
endAngle = 360
ellipse = cv2.ellipse(ellipse, center, axesLength, angle, startAngle, endAngle, Shapes.random_color(), -1)
return ellipse
@staticmethod
def dist(p,q):
return ((p[0]-q[0])**2 + (p[1]-q[1])**2)**0.5
@staticmethod
def create_triangle():
triangle = np.zeros((50, 50, 3), np.uint8)
boolean = True
while(boolean):
p1 = [r.randint(0, 20), r.randint(0, 50)]
p2 = [r.randint(0, 25), r.randint(0, 25)]
p3 = [r.randint(35, 50), r.randint(0, 50)]
boolean = ((p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) + p3[0] * (p1[1] - p2[1]) ) / 2) < 100 # verify area of triangle to avoid wierd line shapes
points = np.array([p1, p2, p3])
color = Shapes.random_color()
triangle = cv2.fillConvexPoly(triangle, points, color)
triangle = cv2.line(triangle, tuple(p1), tuple(p2), color, 1)
triangle = cv2.line(triangle, tuple(p2), tuple(p3), color, 1)
triangle = cv2.line(triangle, tuple(p3), tuple(p1), color, 1)
return triangle
@staticmethod
def create_rect():
start_point = (r.randint(10,30), r.randint(10,30))
h = r.randint(8,50-start_point[0])
w = r.randint(8,50-start_point[1])
end_point = (h+start_point[0],w+start_point[1])
thickness = -1
rect = np.zeros((50, 50, 3)).astype(np.uint8)
rect = cv2.rectangle(rect, start_point, end_point, Shapes.random_color(), thickness)
return rect
@staticmethod
def random_color():
return [r.randint(10,255),r.randint(10,255),r.randint(10,255)]
@staticmethod
def create_by_category():
"""
0 - Rectangle
1 - Triangle
2 - Ellipse
"""
categories = [ [],[],[] ]
for i in range(10):
rect = Shapes.create_rect()
tri = Shapes.create_triangle()
elipse = Shapes.create_ellipse()
categories[0].append(rect)
categories[1].append(tri)
categories[2].append(elipse)
return categories
@staticmethod
def load_data(test_percentage=0.2):
augmented_dataset = [[],[],[]]
labels = ["Rectangle", "Triangle", "Ellipse"]
data_set = Shapes.create_by_category()
for index in range (3):
for img in data_set[index]:
current_augmentations = RandomAugmentation.generate_augmentations(img, 4, 0.5)
augmented_dataset[index].append(img)
for img2 in current_augmentations:
augmented_dataset[index].append(img2)
x_train = []
x_test = []
y_train = []
y_test = []
count = 0
for i in range(3):
for example in augmented_dataset[i]:
if count < len(augmented_dataset[i]) * (1-test_percentage):
x_train.append(example)
y_train.append(labels[i])
else:
x_test.append(example)
y_test.append(labels[i])
x_train = np.asarray(x_train)
y_train = np.asarray(y_train)
x_test = np.asarray(x_test)
y_test = np.asarray(y_test)
return (x_train, y_train), (x_test, y_test)
data_set = Shapes.create_by_category()
rect_labels = ["Rectangle" for i in range(5)]
triangle_labels = ["Triangle" for i in range(5)]
ellipse_labels = ["Ellipse" for i in range(5)]
Image.show_all(data_set[0][0:5] , rect_labels)
Image.show_all(data_set[1][0:5], triangle_labels)
Image.show_all(data_set[2][0:5], ellipse_labels)
(x_train, y_train), (x_test, y_test) = Shapes.load_data(0) #chose 100% of the examples into the x_train y_train.
for i in range(0,len(x_train),5):
Image.show_all(x_train[i:i+5] , y_train[i:i+5])